﻿// Copyright (c) 2012 - 2020 Jared Zheng <jaredz at outlook dot com>
// Buddha Framework is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//          http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
//
// Create : 2012-12-01 version 0.1 Jared Zheng <jaredz at outlook dot com>
// Update : 2018-12-05 version 3.1.5 Jared Zheng <jaredz at outlook dot com>

#include "stdafx.h"
#include "CommonServer.h"
#include "servicepacket.h"
#include "RoutineImp.h"
#include "routinedef.h"

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// CServiceRoutine
IMPLEMENT_RTTI_CREATE( CServiceRoutine, CObject, CObjectTraits::OBJECTT_NONE )

UInt CServiceRoutine::OnHandle(Int nEvent, uintptr_t utData, ULLong ullParam)
{
    switch (nEvent)
    {
    case PAKE_LINKACK:
        {
            if (ullParam == PAKT_MASTER)
            {
#ifdef __DUMP_STEP_DETAIL__
                DEV_DUMP(TF("[业务功能例程]业务管理连接[id=%d]成功, 如果业务数据加载完成, 可以向业务管理注册业务!"), utData);
#endif
                m_uMasterId = (UInt)utData;
                m_pServer->GetMgrHandler()->OnHandle(PAKE_SYNC, utData, PAKT_MASTER);
            }
        }
        break;
    case PAKE_UNLINKACK:
        {
            if (ullParam == PAKT_MASTER)
            {
#ifdef __DUMP_STEP_DETAIL__
                DEV_WARN(TF("[业务功能例程]业务管理连接[id=%d]断开, 对维护的外网端标识为内部服务器异常离线状态, 重置数据准备连接业务管理成功以后重新注册!"), m_uMasterId);
#endif
                m_uMasterId = 0;
                CAtomics::Exchange64<LLong>((PLLong)&m_ullLinkRId, 0);
                m_pServer->GetServerInfo(PAKT_SERVICE)->ClearOnline();
                m_pServer->GetMgrHandler()->OnHandle(PAKE_SYNC, 0, PAKT_MASTER);
            }
        }
        break;
    case PAKE_CONFIG_LINK:
        {
            if (m_uMasterId == 0)
            {
                return ROUTINE_ERROR_REQ_MASTER;
            }
            if (m_ullLinkRId != 0)
            {
                return ROUTINE_ERROR_LINK_ONLINE;
            }

            // 多业务标号的需要每个业务例程保存一个连接状态
            CString strName;
            m_BusinessPtr->GetName(strName);

            CNETSrvLink Link;
            Link.SetType(PAKT_SERVICE);
            Link.SetSrvId((USRVID)m_pServer->GetServerInfo(PAKT_SERVICE)->usId);
            Link.SetSessionId(utData);

            if (m_bRIDOnly)
            {
                LOGV_DUMP(m_pServer->GetFileLog(), TF("[业务功能例程]业务功能RID=%llX开始注册[RIDOnly]"), utData);
                m_pServer->GetNetworkPtr()->Send(m_pServer->GetSocket(PAKT_MASTER), Link);
            }
            else
            {
                LOGV_DUMP(m_pServer->GetFileLog(), TF("[业务功能例程]业务功能Name=%s[RID=%llX]开始注册"), *strName, CHash::Hash64(*strName));
                CStream& Stream = Link.Alloc(m_pServer->GetNetworkPtr());
                Stream << strName;

                Link.Send(m_pServer->GetSocket(PAKT_MASTER), m_pServer->GetNetworkPtr());
            }
        }
    default: {}
    }
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->OnHandle(nEvent, utData, ullParam);
    }
    return RET_OKAY;
}

UInt CServiceRoutine::OnHandle(Int nEvent, CEventBase& EventRef, ULLong ullParam)
{
    switch (nEvent)
    {
    case PAKE_SERVICE_LOGIN: // 网关通知在线业务发送在线RID信息
        {
            CNETSrvLogin* pLogin = static_cast<CNETSrvLogin*>(&EventRef);
#ifdef __DUMP_STEP_DETAIL__
            DEV_DUMP(TF("[业务功能例程]业务网关[id=%d]通知在线注册成功的业务服务器发送RID信息, 如果还没有注册, 忽略本次消息!"), pLogin->GetSrvId());
#endif
            if (m_ullLinkRId != 0)
            {
                LOGV_DUMP(m_pServer->GetFileLog(), TF("[业务功能例程]业务网关[id=%d]通知在线注册成功的业务服务器发送RID信息, 通知网关RID=%llX在线!"), pLogin->GetSrvId(), m_ullLinkRId);
                CPAKSrvLoginAck Ack;
                Ack.SetType(PAKT_SERVICE);
                Ack.SetSrvId(pLogin->GetSrvId());
                Ack.SetSessionId(m_ullLinkRId);
                m_pServer->GetNetworkPtr()->Send(m_pServer->GetSocket(PAKT_MASTER), Ack);
            }
            else
            {
                LOGV_DUMP(m_pServer->GetFileLog(), TF("[业务功能例程]业务网关[id=%d]通知在线注册成功的业务服务器发送RID信息, 因为还没有注册, 忽略本次消息!"), pLogin->GetSrvId());
            }
        }
        break;
    case PAKE_SERVICE_LOGOUT: // 网关离线
        {
            CNETSrvLogout* pAck = static_cast<CNETSrvLogout*>(&EventRef);
#ifdef __DUMP_STEP_DETAIL__
            DEV_DUMP(TF("[业务功能例程]业务网关[id=%d]离线!"), pAck->GetSrvId());
#endif
            ClearGateTotal(pAck->GetSrvId());
        }
        break;
    case PAKE_SERVICE_LINKACK:
        {
            CNETSrvLinkAck* pAck = static_cast<CNETSrvLinkAck*>(&EventRef);
            CAtomics::Exchange64<LLong>((PLLong)&m_ullLinkRId, (LLong)pAck->GetSessionId());
            //m_ullLinkRId = pAck->GetSessionId();
            LOGV_DUMP(m_pServer->GetFileLog(), TF("[业务功能例程]业务注册RID=%llX返回结果=%d!"), pAck->GetSessionId(), pAck->GetAck());
#ifdef __DUMP_STEP_DETAIL__
            DEV_DUMP(TF("[业务功能例程]业务注册RID=%llX返回结果=%d!"), pAck->GetSessionId(), pAck->GetAck());
#endif
            m_pServer->GetMgrHandler()->OnHandle(PAKE_UPDATE, pAck->GetSessionId(), pAck->GetAck());
        }
        break;
    case PAKE_SERVICE_UNLINKACK:
        {
            LOGV_DUMP(m_pServer->GetFileLog(), TF("[业务功能例程]业务注销RID=%llX!"), m_ullLinkRId);
            CAtomics::Exchange64<LLong>((PLLong)&m_ullLinkRId, 0);
            //m_ullLinkRId = 0;
#ifdef __DUMP_STEP_DETAIL__
            CNETSrvUnlinkAck* pAck = static_cast<CNETSrvUnlinkAck*>(&EventRef);
            DEV_DUMP(TF("[业务功能例程]业务注销RID=%llX返回结果=%d!"), pAck->GetAck(), pAck->GetSessionId());
#endif
        }
        break;
    case PAKE_ROUTINE_LINK:
        {
            CNETRoutine* pRoutine = static_cast<CNETRoutine*>(&EventRef);
#ifdef __DUMP_STEP_DETAIL__
            DEV_DUMP(TF("[业务功能例程]收到网关[id=%d]的外网端[%p]连接通知!"), pRoutine->GetSrvId(), pRoutine->GetSessionId());
#endif
            IncrGateTotal(pRoutine->GetSrvId());
        }
        break;
    case PAKE_ROUTINE_UNLINK:
        {
            CNETRoutine* pRoutine = static_cast<CNETRoutine*>(&EventRef);
#ifdef __DUMP_STEP_DETAIL__
            DEV_DUMP(TF("[业务功能例程]收到网关[id=%d]的外网端[%p]断开通知!"), pRoutine->GetSrvId(), pRoutine->GetSessionId());
#endif
            DecrGateTotal(pRoutine->GetSrvId());
        }
        break;
    default:
        {
            // 收到业务事件
        }
    }
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->OnHandle(nEvent, EventRef, ullParam);
    }
    return RET_OKAY;
}

UInt CServiceRoutine::OnHandle(Int nEvent, CStream& Stream, ULLong ullParam)
{
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->OnHandle(nEvent, Stream, ullParam);
    }
    return RET_OKAY;
}

bool CServiceRoutine::Start(ICommonServer* pServer)
{
    #pragma message("[业务功能例程]未支持多业务编号")
    m_pServer = pServer;
    if ((m_pServer != nullptr) && LoadExtConfig())
    {
        // 默认必须创建异步事件队列
        m_uThreads = (UInt)m_pServer->GetConfig().GetServerValue(CServerConfig::CFG_LOAD_SERVICE);
        m_uThreads = CPlatform::CheckThread(m_uThreads);
        LOGV_INFO(m_pServer->GetFileLog(), TF("[业务功能例程]异步事件队列处理线程=%d, %s"), m_uThreads, (m_uThreads > 1) ? TF("启用多线程并发处理") : TF("启用单线程处理"));
        m_nUpdate    = 0;
        m_ullLinkRId = 0;
        // 超时为更新周期的倍数
        SetTimeout();

        m_GateIdTotal.SetHash(MAP_HASH_MIN);

        if (m_BusinessPtr != nullptr)
        {
            if (m_BusinessPtr->Start(pServer) == false) // 注意异步事件队列还未初始化
            {
                return false;
            }
        }
        if (CEventQueue::EventQueue(m_EventQueuePtr, *this, 0, (Int)m_uThreads))
        {
            m_EventQueuePtr->SetThreadEvent(PAKE_THREAD_START, PAKE_THREAD_STOP);
            return m_EventQueuePtr->Init();
        }
        return true;
    }
    return false;
}

bool CServiceRoutine::Pause(bool bPause)
{
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->Pause(bPause);
    }
    return false;
}

bool CServiceRoutine::Update(void)
{
    if (m_pServer->GetServerInfo(PAKT_SERVICE)->IsMayDay() == false)
    {
        if (m_EventQueuePtr->CheckThreads(m_uTimeout))
        {
            m_pServer->GetServerInfo(PAKT_SERVICE)->MayDay();
            DEV_ERROR(TF("[业务功能例程]事件队列线程异常, 进入异常状态"));
            LOG_ERROR(m_pServer->GetFileLog(), TF("[业务功能例程]事件队列线程异常, 进入异常状态"));
        }
    }
    // 定时更新线程不需要检测
    // 定时更新线程异常导致不向同步管理发送心跳包, 会导致本服务器离线

    // 定时检测扩展配置文件是否有修改
    ++m_nUpdate;
    if (m_nUpdate >= m_nTime)
    {
        m_nUpdate = 0;
        LoadExtConfig(true);
    }
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->Update();
    }
    return true;
}

void CServiceRoutine::Stop(void)
{
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->Stop();
        CString strName = m_BusinessPtr->GetRTTI().GetName();
        m_BusinessPtr->Exit();
        m_BusinessPtr = nullptr;
        CObjectTraits::Unload<ICommonRoutine>(*strName);
    }
    if (m_EventQueuePtr != nullptr)
    {
        m_EventQueuePtr->Exit();
        m_EventQueuePtr = nullptr;
    }
    m_GateIdTotal.RemoveAll();

    m_pServer      = nullptr;
    m_nUpdate      = 0;
    m_uThreads     = 0;
    m_uTimeout     = 0;
    m_uMasterId    = 0;
    m_ullLinkRId   = 0;
    m_bRIDOnly     = true;
}

bool CServiceRoutine::SetTimeout(UInt uTimeout)
{
    if (uTimeout == 0)
    {
        m_uTimeout = m_pServer->GetConfig().GetUpdateTick() * DATAD_TIME;
    }
    else
    {
        m_uTimeout = uTimeout;
        m_uTimeout = DEF::Max<UInt>(1, m_uTimeout);
    }
    return true;
}

bool CServiceRoutine::AddQueue(Int nEvent, uintptr_t utData, ULLong ullParam, QUEUE_TYPE eType)
{
    if (m_pServer == nullptr)
    {
        return false;
    }
    // 入异步事件队列前的检测尽量不要处理复杂功能, 需要处理的建议进队列回调处理
    // 有加锁的建议进队列回调处理
    // "ServerAck"
    // PAKE_LINKACK/PAKE_UNLINKACK, id, master/masterdb
    bool bRet = false;
    if (m_BusinessPtr != nullptr)
    {
        bRet = m_BusinessPtr->AddQueue(nEvent, utData, ullParam, eType);
    }
    switch (nEvent)
    {
    case PAKE_LINKACK:    // 向管理服务器注册回复结果
    case PAKE_UNLINK:     // 向本服务器注册的注销/断开
    case PAKE_UNLINKACK:  // 向管理服务器注销/断开回复结果
    case PAKE_TCP_CLOSED: // 连接的外网端注销/断开
        {
            bRet = true;
        }
        break;
    default: {}
    }
    if (bRet)
    {
        bRet = m_EventQueuePtr->Add(nEvent, utData, ullParam, eType);
    }
    return bRet;
}

bool CServiceRoutine::AddQueue(Int nEvent, CEventBase& EventRef, ULLong ullParam, QUEUE_TYPE eType)
{
    if (m_pServer == nullptr)
    {
        return false;
    }
    // 入异步事件队列前的检测尽量不要处理复杂功能, 需要处理的建议进队列回调处理
    // 有加锁的建议进队列回调处理
    // "CommonEvent"
    // [PAKE_COMMON_BEGIN, PAKE_COMMON_END], EventRef, PTCP_PARAM
    //
    // [PAKE_ROUTINE_BEGIN, PAKE_ROUTINE_FAIL], EventRef, PTCP_PARAM
    // [PAKE_SERVICE_BEGIN, PAKE_SERVICE_END], EventRef, PTCP_PARAM
    bool bRet = false;
    if (m_BusinessPtr != nullptr)
    {
        bRet = m_BusinessPtr->AddQueue(nEvent, EventRef, ullParam, eType);
    }
    ULLong ullSocket = ullParam;
    if (eType > QUEUE_TYPE_MASK)
    {
        CNETTraits::PTCP_PARAM pTcp = reinterpret_cast<CNETTraits::PTCP_PARAM>(ullParam);
        ullSocket = pTcp->sSocket;
    }
    if ((nEvent >= PAKE_ROUTINE_BEGIN) && (nEvent <= PAKE_ROUTINE_FAIL))
    {
        bRet = true;
        (static_cast<CNETRoutine*>(&EventRef))->Attach(ullParam, eType, m_pServer->GetNetworkPtr());
    }
    else if ((nEvent >= PAKE_SERVICE_BEGIN) && (nEvent <= PAKE_SERVICE_END))
    {
        bRet = true;
        (static_cast<CNETSession*>(&EventRef))->Attach(ullParam, eType, m_pServer->GetNetworkPtr());
    }
    if (bRet)
    {
        bRet = m_EventQueuePtr->Add(nEvent, EventRef, ullSocket, (eType & QUEUE_TYPE_MASK));
    }
    return bRet;
}

INLINE bool CServiceRoutine::AddQueue(Int nEvent, CStream& Stream, ULLong ullParam)
{
    if (m_pServer == nullptr)
    {
        return false;
    }
    // 入异步事件队列前的检测尽量不要处理复杂功能, 需要处理的建议进队列回调处理
    // 有加锁的建议进队列回调处理
    // "SyncInfo"---注意流对象可能是临时实例, 不能加入队列
    // PAKE_LINK/PAKE_UPDATE, Stream, master/masterdb
    bool bRet = false;
    if (m_BusinessPtr != nullptr)
    {
        bRet = m_BusinessPtr->AddQueue(nEvent, Stream, ullParam);
    }
    if (bRet)
    {
        bRet = m_EventQueuePtr->Add(nEvent, Stream, ullParam);
    }
    return bRet;
}

bool CServiceRoutine::LoadExtConfig(bool bUpdate)
{
    CStringFix strPath;
    m_pServer->GetConfig().GetServerExtConfig(CServerConfig::CFG_LOAD_SERVICE, strPath);

    CFileAttr fa;
    m_pServer->GetConfig().GetFilePath().FileAttr(fa, *strPath);
    if (bUpdate)
    {
        if (fa.MTime(m_tExtConfig))
        {
            return true;
        }
        LOGV_INFO(m_pServer->GetFileLog(), TF("[业务功能例程]定时读取文件修改时间%llX---%llX"), fa.MTime(), m_tExtConfig);
        m_tExtConfig = fa.MTime();
    }
    else
    {
        m_tExtConfig = fa.MTime();
        LOGV_INFO(m_pServer->GetFileLog(), TF("[业务功能例程]初始化读取文件修改时间=%llX"), m_tExtConfig);
    }
    LOGV_INFO(m_pServer->GetFileLog(), TF("[业务功能例程]ExtConfig=%s"), *strPath);

    CFileReadStream Reader;
    if (Reader.Create(*strPath))
    {
        CKVNode  KVNode;
        CKVStore KVStore;
        if (KVStore.Load(KVNode, Reader))
        {
            if (KVNode.Find(TF("Time")))
            {
                m_nTime = KVNode[TF("Time")].GetInt();
            }
            if (KVNode.Find(TF("RIDOnly")))
            {
                m_bRIDOnly = KVNode[TF("RIDOnly")].GetBoolean();
            }

            bool bRet = true;
            if ((bUpdate == false) && (KVNode.Find(TF("BusinessRoutine"))))
            {
                bRet = LoadExtBusiness(KVNode);
            }
            if (m_BusinessPtr != nullptr)
            {
                bRet = (m_BusinessPtr->OnHandle(bUpdate ? PAKE_CONFIG_UPDATE : PAKE_CONFIG_LOAD,
                                                reinterpret_cast<uintptr_t>(&KVNode),
                                                (ULLong)m_pServer) == RET_OKAY);
            }
            return bRet;
        }
        else
        {
            LOGV_WARN(m_pServer->GetFileLog(), TF("[业务功能例程]解析ExtConfig=%s文件内容失败"), *strPath);
        }
    }
    else
    {
        LOGV_WARN(m_pServer->GetFileLog(), TF("[业务功能例程]打开ExtConfig=%s文件不存在"), *strPath);
    }
    return true;
}

bool CServiceRoutine::LoadExtBusiness(CKVNode& Root)
{
    UInt uCount = 0;

    CKVString strName;
    CKVString strObject;
    CKVString strModule;

    CKVNode& Routine = Root[TF("BusinessRoutine")];
    Routine[TF("Name")].GetString(strName);
    Routine[TF("Object")].GetString(strObject);
    Routine[TF("Module")].GetString(strModule);
#if (__PLATFORM_TARGET__ == PLATFORM_TARGET_LINUX)
    strModule.Insert(0, CServerConfig::LibPrefix);
#endif
    strModule += CServerConfig::LibExt;

    assert(m_BusinessPtr == nullptr);
    // !!!业务例程名称需要使用UUID
    if (CObjectTraits::Load<ICommonRoutine>(*strName, *strModule, m_BusinessPtr, *strObject))
    {
        LOGV_INFO(m_pServer->GetFileLog(), TF("[业务功能例程]RTTI load --- Object=%s, Name=%s, Module=%s --- %p"), *strObject, *strName, *strModule, m_BusinessPtr.Get());
        if (m_BusinessPtr->Init() == (UInt)RET_OKAY)
        {
            m_BusinessPtr->OnHandle(PAKE_CONFIG_DEFAULT, reinterpret_cast<uintptr_t>(this), (ULLong)m_pServer);
            ++uCount;
            if (m_bRIDOnly)
            {
                m_pServer->GetMgrHandler()->OnHandle(PAKE_LINK, reinterpret_cast<uintptr_t>(strName.GetBuffer()), PAKT_SERVICE_MASTER);
            }
            else
            {
                m_pServer->GetMgrHandler()->OnHandle(PAKE_LINK, reinterpret_cast<uintptr_t>(strName.GetBuffer()), PAKT_SERVICE);
            }
            return true;
        }
    }
    else
    {
        LOGV_ERROR(m_pServer->GetFileLog(), TF("[业务功能例程]RTTI load --- Object=%s, Name=%s, Module=%s --- Failed!"), *strObject, *strName, *strModule);
    }
    m_pServer->GetServerInfo(PAKT_SERVICE)->ClearLoad();
    m_pServer->GetMgrHandler()->OnHandle(PAKE_SYNC, uCount, PAKT_MAX);
    return (uCount > 0);
}

bool CServiceRoutine::IncrGateTotal(UInt uId)
{
    CSyncLockWaitScope scope(m_GateIdTotalLock);
    ID_TOTAL_MAP_PAIR* pPair = m_GateIdTotal.Find(uId);
    if (pPair != nullptr)
    {
        pPair->m_V += 1;
    }
    else
    {
        m_GateIdTotal.Add(uId, 1);
    }
    m_pServer->GetServerInfo(PAKT_SERVICE)->IncrTotal(false);
    return true;
}

bool CServiceRoutine::DecrGateTotal(UInt uId)
{
    CSyncLockWaitScope scope(m_GateIdTotalLock);
    ID_TOTAL_MAP_PAIR* pPair = m_GateIdTotal.Find(uId);
    if ((pPair != nullptr) && (pPair->m_V > 0))
    {
        pPair->m_V -= 1;
    }
    m_pServer->GetServerInfo(PAKT_SERVICE)->DecrTotal(false);
    return true;
}

bool CServiceRoutine::ClearGateTotal(UInt uId)
{
    UInt uTotal = 0;

    CSyncLockWaitScope scope(m_GateIdTotalLock);
    ID_TOTAL_MAP_PAIR* pPair = m_GateIdTotal.Find(uId);
    if (pPair != nullptr)
    {
        uTotal = pPair->m_V;
        m_GateIdTotal.RemoveAt(reinterpret_cast<PINDEX>(pPair));
    }
    m_pServer->GetServerInfo(PAKT_SERVICE)->DecrTotal(false, uTotal);
    return true;
}

#if (__PLATFORM_TARGET__ == PLATFORM_TARGET_WINDOWS)
// Windows Realse版本MSVC编译器如果不强制调用静态常量, 静态常量会被优化掉
BOOL WINAPI DllMain(HMODULE hModule, ULong ulReason, void* lpReserved)
{
    UNREFERENCED_PARAMETER( lpReserved );

    if (ulReason == DLL_PROCESS_ATTACH)
    {
        ::DisableThreadLibraryCalls(hModule);
        DEV_DUMP(TF("$ Link %s.dll --- DLL_PROCESS_ATTACH"), CServiceRoutine::ms_RTTI.GetName());
    }
    else if (ulReason == DLL_PROCESS_DETACH)
    {
        DEV_DUMP(TF("$ Unlink %s.dll --- DLL_PROCESS_DETACH"), CServiceRoutine::ms_RTTI.GetName());
    }
    return TRUE;
}
#endif
